function Packet() {
    // noinspection JSUnusedLocalSymbols
    var header;//{UmspHeader}
    // noinspection JSUnusedLocalSymbols
    var payload;//*
    // noinspection JSUnusedLocalSymbols
    var buf;//{DataView}
}

function UmspHeader() {
    this.size = 0;
    this.x = 0;
    this.userID = 0;
    this.gameID = 0;
    this.serviceID = 0;
    this.cmd = 0;
    this.payload = "";
    this.toString = function () {
        return "size:" + this.size
        +" ,roomID:" + this.x
        +" ,userID:" + this.userID
        +" ,gameID:" + this.gameID
        +" ,serviceID:" + this.serviceID
        +" ,cmd:[" + CmdToString(this.cmd)+"]";
    };
}


/**
 * Encoder && Decoder
 * @constructor
 */
function UmspProtocol() {
    var mUserID = 0;
    var mToken = "";
    var mGameID = 0;
    var mServerID = 0;
    var mRoomID = 0;
    var protocol = this;

    this.setRoomID = function (roomID) {
        this.mRoomID = roomID;
    };

    /**
     *
     * @param {Uint8Array} dataArray
     * @param cmd {int}
     * @returns {DataView}
     */
    this.fillHeader = function (dataArray, cmd) {
        var length = (dataArray ? dataArray.length : 0);
        var buffer = new ArrayBuffer(FIXED_HEAD_SIZE + length);
        var dataView = new DataView(buffer);
        dataView.setInt32(0, buffer.byteLength, true);
        dataView.setInt32(4, Number(mRoomID), true);
        dataView.setInt32(8, Number(mUserID), true);
        dataView.setInt32(12, Number(mGameID), true);
        dataView.setInt32(16, Number(mServerID), true);
        dataView.setInt32(20, Number(cmd), true);
        for (var i = 0; i < length; i++) {
            dataView.setUint8(i + FIXED_HEAD_SIZE, dataArray[i]);
        }
        log.i("[encode] cmd: [" +CmdToString(cmd)+"]" );
        return dataView;
    };
    /**
     *
     * @param msg {DataView}
     * @returns {UmspHeader}
     */
    this.parseHeader = function (msg) {
        var dataView = msg;
        var head = new UmspHeader();
        head.size = dataView.getInt32(0, true);
        head.x = dataView.getInt32(4, true);
        head.userID = dataView.getInt32(8, true);
        head.gameID = dataView.getInt32(12, true);
        head.serviceID = dataView.getInt32(16, true);
        head.cmd = dataView.getInt32(20, true);
        return head;
    };
    /**
     *
     * @param msg {DataView}
     * @returns {Packet}
     */
    this.decode = function (msg) {
        var header = this.parseHeader(msg);
        log.i("[decode]:"+header);
        var ext = new Uint8Array(header.size - FIXED_HEAD_SIZE);
        for (var i = 0; i < ext.length; i++) {
            ext[i] = msg.getUint8(FIXED_HEAD_SIZE + i);
        }
        header.payload = ext;

        var packet = new Packet();
        packet.header = header;
        packet.buf = msg;
        packet.payload = ext;
        return packet;
    };

    this.Encoders = {};
    this.Encoders[Umsp.HEARTBEAT] = function () {
        return protocol.fillHeader(new Uint8Array(0), Umsp.HEARTBEAT);
    };
    this.Encoders[Umsp.LOGIN] = function (args) {
        mUserID = args[0];
        mToken = args[1];
        mGameID = args[2];
        mServerID = args[3] || 0;
        // log.i("[REQ]login...userID:" + mUserID);
        return protocol.fillHeader(stringToUtf8ByteArray(args[2]), Umsp.LOGIN);
    };
    this.Encoders[Umsp.MATCH] = function (args) {
        // log.i("[REQ]match...match:" + args[0]);
        return protocol.fillHeader(stringToUtf8ByteArray(JSON.stringify(args[0])), Umsp.MATCH);

    };
    this.Encoders[Umsp.ROOM_ENTER] = function (args) {
        // log.i("[REQ]ROOM_ENTER... roomSession:" + args[0]);
        return protocol.fillHeader(stringToUtf8ByteArray(args[0]), Umsp.ROOM_ENTER);

    };
    this.Encoders[Umsp.MSG] = function (args) {
        // log.i("[REQ]ROOM_MSG...:" + args[0]);
        return protocol.fillHeader(stringToUtf8ByteArray(args[0]), Umsp.MSG);
    };
    this.encode = function () {
        var cmd = arguments[0];
        if (!this.Encoders[cmd]) {
            log.e("Not Found Encoder For " + cmd + " (" + CmdToString(cmd) + ")");
        } else {
            var args = [];
            for (var i = 1; i < arguments.length; i++) {
                args.push(arguments[i]);
            }
            return this.Encoders[arguments[0]](args);
        }
    };

    this.getRoomList = function (gameID, filter) {

        //todo  getRoomList
    };


    this.stopJoin = function (gameID, roomID, cpProto, userID) {
        //todo  stopJoin
    };

}

